package com.cee.sch.job.service.impl;


import com.cee.enums.LastRecTxnType;
import com.cee.exception.SystemException;
import com.cee.quartz.bean.JobDetailDTO;
import com.cee.quartz.component.QuartzUtil;
import com.cee.quartz.job.EnableIndEnum;
import com.cee.quartz.job.JobType;
import com.cee.quartz.job.http.HttpJobDataDTO;
import com.cee.quartz.job.http.HttpQuartzJob;
import com.cee.sch.job.bean.ScheduleJobSettingBean;
import com.cee.sch.job.dao.ScheduleJobMapper;
import com.cee.sch.job.dto.ScheduleJobDTO;
import com.cee.sch.job.model.ScheduleJob;
import com.cee.sch.job.service.ScheduleJobService;
import com.cee.util.AuthContextHolder;
import com.cee.util.BeanUtil;
import com.cee.util.JsonUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import org.apache.commons.lang3.StringUtils;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Transactional
@Service
public class ScheduleJobServiceImpl implements ScheduleJobService {

    public final Logger logger = LoggerFactory.getLogger(ScheduleJobServiceImpl.class);

    @Autowired
    private ScheduleJobMapper scheduleJobMapper;

    @Autowired
    private QuartzUtil quartzUtil;
    @Autowired
    private ScheduleJobSettingBean scheduleJobSettingBean;

    @Override
    public PageInfo<ScheduleJobDTO> getScheduleJobPageInfo(ScheduleJobDTO query) {
        Page<ScheduleJobDTO> startPage = PageHelper.startPage(query.getPagination().getPageNum(), query.getPagination().getPageSize());
        scheduleJobMapper.getScheduleJobList(query);
        return new PageInfo<>(startPage);
    }

    @Override
    public ScheduleJobDTO addScheduleJob(ScheduleJobDTO scheduleJob) throws SchedulerException, ParseException {

        //验证 jobName、jobGroupName 是否已存在。  存在：不能add
        //update 业务 不需要验证。(前端传入参数AddJobForUpdate)
        ScheduleJobDTO checkJobDTO = scheduleJobMapper.getByNameAndGroup(scheduleJob.getJobName(), scheduleJob.getJobGroupName());
        if (checkJobDTO != null) {
            throw new SystemException("jobName '{}' and jobGroupName '{}' is Already exist",checkJobDTO.getJobName(),checkJobDTO.getJobGroupName());
        }

        JobDetailDTO jobDetail = convertTo(scheduleJob);

        if (StringUtils.equals(JobType.LOCAL.getCode(), scheduleJob.getJobType())) {
            addOrUpdateQuartzJob(jobDetail, getClass(scheduleJob.getClassName()));
        } else if (StringUtils.equals(JobType.HTTP.getCode(), scheduleJob.getJobType())) {
            addOrUpdateQuartzJob(jobDetail, (Class<? extends Job>) HttpQuartzJob.class);
        }

        scheduleJobMapper.insert(scheduleJob);
        if (StringUtils.equals(EnableIndEnum.N.getCode(), scheduleJob.getEnabledInd())) {
            quartzUtil.pauseJob(scheduleJob.getJobName(), scheduleJob.getJobGroupName());
        }

        return scheduleJob;
    }

    private Class<? extends Job> getClass(String className) {
        try {
            return (Class<? extends Job>) Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e);
        }
    }

    private void addOrUpdateQuartzJob(JobDetailDTO jobDetail, Class<? extends Job> jobClass) throws SchedulerException {
        quartzUtil.addOrUpdateJob(jobDetail.getJobName(), jobDetail.getJobGroupName(), jobDetail.getDescription(), jobDetail.getCron(), jobClass, jobDetail.getJobData());
    }

    private JobDetailDTO convertTo(ScheduleJobDTO scheduleJob) {
        JobDetailDTO jobDetail = new JobDetailDTO();
        // 验证是否json格式
        try {
            BeanUtil.copyProperties(scheduleJob, jobDetail);
            Map<String, Object> jobDataMap = StringUtils.isNotEmpty(scheduleJob.getJobDataJson()) ? JsonUtil.parse(scheduleJob.getJobDataJson(), Map.class) : null;

            if (StringUtils.equals(JobType.LOCAL.getCode(), scheduleJob.getJobType())) {
                if (jobDataMap != null) {
                    jobDetail.setJobData(new JobDataMap(jobDataMap));
                } else {
                    jobDetail.setJobData(new JobDataMap());
                }
            } else if (StringUtils.equals(JobType.HTTP.getCode(), scheduleJob.getJobType())) {
                HttpJobDataDTO httpJobData = scheduleJob.getHttpJobData();
                httpJobData.setRequestBody(jobDataMap);

                JobDataMap jobData = new JobDataMap();
                jobData.put(HttpQuartzJob.HTTP_JOB_DATA_KEY, JsonUtil.format(httpJobData));
                jobDetail.setJobData(jobData);
            }

            return jobDetail;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    @Override
    public ScheduleJobDTO updateScheduleJob(ScheduleJobDTO scheduleJobForUpdate) throws SchedulerException, ParseException {

        String jobName = scheduleJobForUpdate.getJobName();
        String jobGroupName = scheduleJobForUpdate.getJobGroupName();

        ScheduleJobDTO existJobDTO = getScheduleJobDetailById(scheduleJobForUpdate.getVisSysSchJobKey());

        Class jobClass = null;
        try {
            jobClass = Class.forName(existJobDTO.getClassName());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e);
        }

        // if jobName or jobGroupName change, then delete old job of quartz
        if (!(StringUtils.equals(existJobDTO.getJobName(), scheduleJobForUpdate.getJobName()) && StringUtils.equals(existJobDTO.getJobGroupName(), scheduleJobForUpdate.getJobGroupName()))) {
            logger.info("old job name: {}, new job name: {}, old job group name: {}, new job group name: {}",
                    existJobDTO.getJobName(), scheduleJobForUpdate.getJobName(),
                    existJobDTO.getJobGroupName(), scheduleJobForUpdate.getJobGroupName());
            quartzUtil.deleteJob(existJobDTO.getJobName(), existJobDTO.getJobGroupName());
        }

        JobDetailDTO jobDetailDTO = convertTo(scheduleJobForUpdate);
        addOrUpdateQuartzJob(jobDetailDTO, jobClass);

        // EnabledInd : Y 启动job  N 暂停 job
        if (StringUtils.equals(EnableIndEnum.Y.getCode(), scheduleJobForUpdate.getEnabledInd())) {
            quartzUtil.resumeJob(jobName, jobGroupName);
        } else if (StringUtils.equals(EnableIndEnum.N.getCode(), scheduleJobForUpdate.getEnabledInd())) {
            quartzUtil.pauseJob(jobName, jobGroupName);
        }

        // update scheduleJobForUpdate
        ScheduleJob updateSchuleJob = new ScheduleJob();
        updateSchuleJob.setVisSysSchJobKey(scheduleJobForUpdate.getVisSysSchJobKey());
        updateSchuleJob.setJobName(jobName);
        updateSchuleJob.setJobGroupName(jobGroupName);
        updateSchuleJob.setEnabledInd(scheduleJobForUpdate.getEnabledInd());
        updateSchuleJob.setJobType(scheduleJobForUpdate.getJobType());
        scheduleJobMapper.updateById(updateSchuleJob);

        return scheduleJobForUpdate;
    }

    @Override
    public void deleteScheduleJob(Long visSysSchJobKey) throws SchedulerException {
        // check if running
        ScheduleJob scheduleJob = scheduleJobMapper.selectById(visSysSchJobKey);

        //update enableInd = N
        ScheduleJob updateScheduleJob = new ScheduleJob();
        updateScheduleJob.setVisSysSchJobKey(visSysSchJobKey);
        updateScheduleJob.setLastRecTxnTypeCode(LastRecTxnType.D.getCode());
        updateScheduleJob.setLastRecDelDate(new Date());
        updateScheduleJob.setLastRecTxnUserId(AuthContextHolder.getUserId().toString());
        updateScheduleJob.setLastRecTxnDate(new Date());
        scheduleJobMapper.updateById(updateScheduleJob);

        //delete quartz job
        quartzUtil.deleteJob(scheduleJob.getJobName(), scheduleJob.getJobGroupName());
    }

    @Override
    public ScheduleJobDTO getScheduleJobByNameAndGroup(String jobName, String jobGroupName) throws SchedulerException {
        if (StringUtils.isEmpty(jobName) || StringUtils.isEmpty(jobGroupName)) {
            throw new SystemException("jobName,jobGroupName is null");
        }
        JobDetail jobDetail = quartzUtil.getJobDetail(jobName, jobGroupName);
        if (jobDetail == null) {
            throw new IllegalArgumentException("the quartz job is not existed");
        }
        ScheduleJobDTO scheduleJobDTO = scheduleJobMapper.getByNameAndGroup(jobName, jobGroupName);
        setupJob(jobDetail, scheduleJobDTO);

        return scheduleJobDTO;
    }

    private void setupJob(JobDetail jobDetail, ScheduleJobDTO scheduleJobDTO) throws SchedulerException {
        if (StringUtils.equals(scheduleJobDTO.getJobType(), JobType.HTTP.getCode())) {
            if (jobDetail.getJobDataMap() != null) {
                //json字符串
                String content = (String) jobDetail.getJobDataMap().get(HttpQuartzJob.HTTP_JOB_DATA_KEY);
                //json字符串 对应的DTO
                HttpJobDataDTO httpJobDataDTO = JsonUtil.parse(content, HttpJobDataDTO.class);

                String jobData = JsonUtil.format(httpJobDataDTO.getRequestBody());

                scheduleJobDTO.setHttpJobData(httpJobDataDTO);
                scheduleJobDTO.setJobDataJson(jobData);
            }
        } else if (StringUtils.equals(scheduleJobDTO.getJobType(), JobType.LOCAL.getCode())) {
            Map jobDataMap = jobDetail.getJobDataMap();
            if (jobDataMap.size() != 0) {
                String jobData = JsonUtil.format(jobDataMap);
                scheduleJobDTO.setJobDataJson(jobData);
            }
        }
    }

    @Override
    public ScheduleJobDTO getScheduleJobDetailById(Long id) throws SchedulerException {
        ScheduleJobDTO scheduleJobDTO = scheduleJobMapper.getById(id);
        if (scheduleJobDTO == null) {
            throw new SystemException("scheduleJobDTO not exists");
        }

        JobDetail jobDetail = quartzUtil.getJobDetail(scheduleJobDTO.getJobName(), scheduleJobDTO.getJobGroupName());
        setupJob(jobDetail, scheduleJobDTO);

        return scheduleJobDTO;
    }

    @Override
    public ScheduleJob getScheduleJobById(Long id) {
        ScheduleJobDTO scheduleJobDTO = scheduleJobMapper.getById(id);
        if (scheduleJobDTO == null) {
            throw new SystemException("scheduleJobDTO '" + scheduleJobDTO.getVisSysSchJobKey() + "' not exists");
        }

        return scheduleJobDTO;
    }

    @Override
    public void disableScheduleJob(long visSysSchJobKey) throws SchedulerException {
        // check if running
        ScheduleJob scheduleJob = scheduleJobMapper.selectById(visSysSchJobKey);

        //update enableInd = N
        ScheduleJob updateScheduleJob = new ScheduleJob();
        updateScheduleJob.setVisSysSchJobKey(visSysSchJobKey);
        updateScheduleJob.setEnabledInd(EnableIndEnum.N.getCode());
        scheduleJobMapper.updateById(updateScheduleJob);

        //pauseJob
        quartzUtil.pauseJob(scheduleJob.getJobName(), scheduleJob.getJobGroupName());
    }

    @Override
    public void enableScheduleJob(long visSysSchJobKey) throws SchedulerException {
        ScheduleJob scheduleJob = scheduleJobMapper.selectById(visSysSchJobKey);

        //update enableInd = Y
        ScheduleJob updateScheduleJob = new ScheduleJob();
        updateScheduleJob.setVisSysSchJobKey(visSysSchJobKey);
        updateScheduleJob.setEnabledInd(EnableIndEnum.Y.getCode());
        scheduleJobMapper.updateById(updateScheduleJob);

        //resumeJob
        quartzUtil.resumeJob(scheduleJob.getJobName(), scheduleJob.getJobGroupName());
    }

    @Override
    public void resetScheduleJob() {
        List<ScheduleJobDTO> scheduleJobList = scheduleJobSettingBean.getScheduleJobList();
        for (ScheduleJobDTO scheduleJobDTO : scheduleJobList) {
            try {
                addScheduleJob(scheduleJobDTO);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

}
